﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace IQIGame.Onigao.Framework
{
    [AddComponentMenu("UIEx/ExButton")]
    public class ExButton : Button, IUGUIEx
    {
        #region override listener
        // 自定义的 UnityEvent 只允许一个侦听器
        [System.Serializable]
        public class SingleListenerUnityEvent : ButtonClickedEvent
        {
            public SingleListenerUnityEvent(UnityAction baseCall)
            {
                base.AddListener(baseCall);
            }

            private UnityAction currentListener;

            public new void AddListener(UnityAction call)
            {
                // 如果已经存在侦听器，则阻止添加
                if (currentListener != null)
                {
                    LogGame.LogError("ExButton: 不能添加多个侦听器。");
                    return;
                }

                base.AddListener(call);
                currentListener = call;
            }

            public new void RemoveListener(UnityAction call)
            {
                base.RemoveListener(call);
                if (currentListener == call)
                {
                    currentListener = null;
                }
            }

            public new void RemoveAllListeners()
            {
                base.RemoveAllListeners();
                currentListener = null;
            }
        }

        [SerializeField]
        private SingleListenerUnityEvent _onClick;

        public new SingleListenerUnityEvent onClick
        {
            get { return _onClick; }
            set { _onClick = value; base.onClick = _onClick; }
        }
        #endregion

        protected ExButton() : base()
        {
            this.onClick = new SingleListenerUnityEvent(this.OnClickSelf);
        }

        public object dynamicData { get; set; }

        public void SetActive(bool isActive)
        {
            this.gameObject.SetActive(isActive);
        }

        public ExText tilte => this.GetComponentInChildren<ExText>();
        public ExImage background => this.GetComponentInChildren<ExImage>();

        private string _text = null;
        public string text
        {
            get { return this._text; }
            set
            {
                if (string.Equals(this._text, value))
                    return;
                this._text = value;
                var tilte = this.tilte;
                if (tilte != null)
                    tilte.text = value;
            }
        }

        [SerializeField] private GameObject _gobjSelected;
        private bool _isSelected = false;
        public bool isSelected => this._isSelected;
        public void SetSelectState(bool isSelected)
        {
            this._isSelected = isSelected;
            if (this._gobjSelected)
                this._gobjSelected.SetActive(isSelected);
        }

        private Action<ExButton> _systemCallClick;

        public Action<ExButton> systemCallClick
        {
            get => _systemCallClick;
            set => _systemCallClick = value;
        }

        private Func<GameObject, bool> _funcCheckIsCanClick;

        public Func<GameObject, bool> funcCheckIsCanClick
        {
            get => _funcCheckIsCanClick;
            set => _funcCheckIsCanClick = value;
        }

        protected virtual void OnClickSelf()
        {
            this.systemCallClick?.Invoke(this);
        }

        private float _durationNextClickTime = 0.5f;
        public float durationNextClickTime { get => _durationNextClickTime; set => _durationNextClickTime = value; }
        public float nextCanClickTime { get; set; }

        protected bool IsCanNextClick
        {
            get
            {
                if (this.nextCanClickTime > Time.realtimeSinceStartup)
                    return false;
                if (this.durationNextClickTime > 0)
                    this.nextCanClickTime = Time.realtimeSinceStartup + this.durationNextClickTime;
                return true;
            }
        }

        public int eventPassFloor = 0;

        public override void OnPointerClick(PointerEventData eventData)
        {
            if (CheckIsCanClick() && IsCanNextClick)
            {
                base.OnPointerClick(eventData);
                PassEvent(eventData, ExecuteEvents.pointerClickHandler, this.eventPassFloor, this.CheckIsCanPass);
            }
        }

        private bool CheckIsCanClick()
        {
            return funcCheckIsCanClick == null || funcCheckIsCanClick(this.gameObject);
        }

        private bool CheckIsCanPass(GameObject go)
        {
            return go?.GetComponentInParent<ExButton>() != null;
        }

        public override void OnSubmit(BaseEventData eventData)
        {
            if (CheckIsCanClick() && IsCanNextClick)
            {
                base.OnSubmit(eventData);
            }
        }

        private void PassEvent<T>(PointerEventData data, ExecuteEvents.EventFunction<T> funcEvent, int passFloor = 0, Func<GameObject, bool> funcFilterCanPass = null) where T : IEventSystemHandler
        {
            if (passFloor == 0)
                return;

            List<RaycastResult> results = new(16);
            EventSystem.current.RaycastAll(data, results);
            GameObject currentGo = data.pointerCurrentRaycast.gameObject;
            for (int i = 0, cnt = results.Count; i < cnt; i++)
            {
                if (passFloor == 0)
                    break;
                var resultGo = results[i].gameObject;
                if (resultGo != currentGo && (funcFilterCanPass == null || funcFilterCanPass(resultGo)))
                {
                    passFloor--;
                    // ExecuteEvents.Execute
                    ExecuteEvents.ExecuteHierarchy<T>(resultGo, data, funcEvent);
                }
            }
        }
    }
}
